#include <engine.h>

#define MAX_LAYER_COUNT 5
#define FRAME_COUNT 60

extern Boolean g_needFlipAll;
extern Boolean g_isSurfaceFlip;
BitmapBufInfo img;

void AddPushAnimation(AnimationLayer* node, GrafixObjectStack* stack, LayerManagerEX* layerManager, int id, int flag)
{
    int i,x,y,height;
    int nLayNum = layerManager->GetNumberOfLayers();
    int* lid = new int[nLayNum];
    if (!lid) return;

    if (!flag) {
        layerManager->GetAllLayerIds(lid);
        for (i=0;i<nLayNum;i++) {
            if (lid[i]==id) {
                layerManager->MoveLayer(id, -node->leadingOffsetX, -node->leadingOffsetY);
                stack->AddAnimation(
                    new cMoveAction(
                            node->leadingOffsetX,
                            node->leadingOffsetY,
                            stack, layerManager, &id,1, EASEOUT, 0, node->leadingSpeed));
            }
            else {
                x = layerManager->GetLayerPosition(lid[i])->x;
                y = layerManager->GetLayerPosition(lid[i])->y;
                height = layerManager->GetLayerBits(lid[i])->height;

                if ((y <= node->y)&&(y+height >= node->y+node->height)) {
                    stack->AddAnimation(
                        new cMoveAction(
                                node->leadingOffsetX,
                                node->leadingOffsetY,
                                stack, layerManager, &lid[i],1, EASEOUT, 0, node->leadingSpeed));
                }
            }
        }
    }
    else {
        layerManager->GetAllLayerIds(lid);
        for (i=0;i<nLayNum;i++) {
            if (lid[i]==id) {
                stack->AddAnimation(
                    new cMoveAction(
                            node->trailingOffsetX,
                            node->trailingOffsetY,
                            stack, layerManager, &id,1, EASEOUT, 0, node->trailingSpeed));
            }
            else {
                x = layerManager->GetLayerPosition(lid[i])->x;
                y = layerManager->GetLayerPosition(lid[i])->y;
                height = layerManager->GetLayerBits(lid[i])->height;

                if ((y <= node->y)&&(y+height >= node->y+node->height)) {
                    layerManager->MoveLayer(lid[i], -node->trailingOffsetX, -node->trailingOffsetY);
                    stack->AddAnimation(
                        new cMoveAction(
                                node->trailingOffsetX,
                                node->trailingOffsetY,
                                stack, layerManager, &lid[i],1, EASEOUT, 0, node->trailingSpeed));
                }
            }
        }
    }
    delete lid;
    return;
}

void AddMenuPushAnimation(AnimationLayer* node, GrafixObjectStack* stack, LayerManagerEX* layerManager, int id, int flag)
{
    int i,x,y,height;
    int nLayNum = layerManager->GetNumberOfLayers();
    int* lid = new int[nLayNum];

    if (!lid) return;

    if (!flag) {
        layerManager->GetAllLayerIds(lid);
        for (i=0;i<nLayNum;i++) {
            if (lid[i]==id) {
                layerManager->MoveLayer(id, -node->leadingOffsetX, -node->leadingOffsetY);
                stack->AddAnimation(
                    new cMoveAction(
                            node->leadingOffsetX,
                            node->leadingOffsetY,
                            stack, layerManager, &id,1, EASEOUT, 0, node->leadingSpeed));
                img = *layerManager->GetLayerBits(id);
                img.height = 40;
                layerManager->AddLayer(img, 0, 24, 255);
            }
            else {
                x = layerManager->GetLayerPosition(lid[i])->x;
                y = layerManager->GetLayerPosition(lid[i])->y;
                height = layerManager->GetLayerBits(lid[i])->height;

                if ((y <= node->y)&&(y+height >= node->y+node->height)) {
                    stack->AddAnimation(
                        new cMoveAction(
                                node->leadingOffsetX,
                                node->leadingOffsetY,
                                stack, layerManager, &lid[i],1, EASEOUT, 0, node->leadingSpeed));
               }
            }
        }
    }
    else {
        layerManager->GetAllLayerIds(lid);
        for (i=0;i<nLayNum;i++) {
            if (lid[i]==id) {
                stack->AddAnimation(
                    new cMoveAction(
                            node->trailingOffsetX,
                            node->trailingOffsetY,
                            stack, layerManager, &id,1, EASEOUT, 0, node->trailingSpeed));
                img = *layerManager->GetLayerBits(id);
                img.height = 40;
                layerManager->AddLayer(img, 0, 24, 255);
            }
            else {
                x = layerManager->GetLayerPosition(lid[i])->x;
                y = layerManager->GetLayerPosition(lid[i])->y;
                height = layerManager->GetLayerBits(lid[i])->height;

                if ((y <= node->y)&&(y+height >= node->y+node->height)) {
                    layerManager->MoveLayer(lid[i], -node->trailingOffsetX, -node->trailingOffsetY);
                    stack->AddAnimation(
                        new cMoveAction(
                                node->trailingOffsetX,
                                node->trailingOffsetY,
                                stack, layerManager, &lid[i],1, EASEOUT, 0, node->trailingSpeed));
                }
            }
        }
    }
    delete lid;
    return;
}

void OnZoomFinish(void * pt)
{
}

int AddLayer(
    /* [in] */ BufferCacheNode* buffer,
    /* [in] */ LayerManagerEX* layerManager,
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 alpha)
{
    BitmapBufInfo image = {
                    buffer->m_buffer.info.address,
                    buffer->m_buffer.info.width,
                    buffer->m_buffer.info.height,
                    buffer->m_buffer.info.pitch,
                    2, buffer->m_buffer.info.bitsPerPixel};

    if (PIXELFORMAT_HAS_ALPHA(buffer->m_buffer.info.pixelFormat)) {
        image.pixelFormat |= 1;
    }

    return layerManager->AddLayer(image, x, y, alpha);
}

Void AddAnime(
    /* [in] */ AnimationLayer* node,
    /* [in] */ GrafixObjectStack* stack,
    /* [in] */ LayerManagerEX* layerManager)
{
    int TmpId = AddLayer(node->buffer, layerManager, node->x, node->y, node->opacity);

    if (node->leadingSpeed < 1) {
        node->leadingSpeed = 60;
    }
    if (node->trailingSpeed < 1) {
        node->trailingSpeed = 60;
    }
    if (node->leadingSpeed > 240) {
        node->leadingSpeed = 240;
    }
    if (node->trailingSpeed > 240) {
        node->trailingSpeed = 240;
    }

    if (node->effect & TransitionEffect_FadeIn) {
        stack->AddAnimation(
                    new cFlashAction(
                            node->leadingOpacity, node->opacity,
                            stack, layerManager, &TmpId,1, NONE, 1,  0, node->leadingSpeed));
    }
    else if (node->effect & TransitionEffect_FadeOut) {
        stack->AddAnimation(
                    new cFlashAction(
                            node->opacity, node->trailingOpacity,
                            stack, layerManager, &TmpId,1, NONE, 1,  0, node->trailingSpeed));
    }

    if (node->effect & TransitionEffect_FlyIn) {
        stack->AddAnimation(
                    new cMoveAction(
                            node->x + node->leadingOffsetX,
                            node->y + node->leadingOffsetY,
                            node->x, node->y,
                            stack, layerManager, &TmpId,1, EASEOUT, 0, node->leadingSpeed));
    }
    else if (node->effect & TransitionEffect_FlyOut) {
        stack->AddAnimation(
                    new cMoveAction(
                            node->x, node->y,
                            node->x + node->trailingOffsetX,
                            node->y + node->trailingOffsetY,
                            stack,
                            layerManager,
                            &TmpId,1, EASEIN, 0, node->trailingSpeed));
    }

    if (node->effect & TransitionEffect_MenuFlyIn) {
        stack->AddAnimation(
                    new cMoveAction(
                            node->x + node->leadingOffsetX,
                            node->y + node->leadingOffsetY,
                            node->x, node->y,
                            stack, layerManager, &TmpId,1, EASEOUT, 0, node->leadingSpeed));

        img = *layerManager->GetLayerBits(TmpId);
        img.height = 40;
        layerManager->AddLayer(img, 0, 24, 255);
    }
    else if (node->effect & TransitionEffect_MenuFlyOut) {
        stack->AddAnimation(
                    new cMoveAction(
                            node->x, node->y,
                            node->x + node->trailingOffsetX,
                            node->y + node->trailingOffsetY,
                            stack,
                            layerManager,
                            &TmpId,1, EASEIN, 0, node->trailingSpeed));
        img = *layerManager->GetLayerBits(TmpId);
        img.height = 40;
        layerManager->AddLayer(img, 0, 24, 255);
    }

    if (node->effect & TransitionEffect_PushIn) {
        AddPushAnimation(node, stack, layerManager, TmpId, 0);
    }
    else if (node->effect & TransitionEffect_PushOut) {
        AddPushAnimation(node, stack, layerManager, TmpId, 1);
    }

    if (node->effect & TransitionEffect_MenuPushIn) {
        AddMenuPushAnimation(node, stack, layerManager, TmpId, 0);
    }
    else if (node->effect & TransitionEffect_MenuPushOut) {
        AddMenuPushAnimation(node, stack, layerManager, TmpId, 1);
    }

    if (node->effect & TransitionEffect_DampingIn) {
        stack->AddAnimation(
                    new cStretchAction(
                            node->width >> 8, node->height,
                            node->width*8/7, node->height,
                            stack, layerManager,
                            &TmpId, 1 , EASEIN | ALIGN_RIGHT, 1, 0, node->leadingSpeed/4*3));
        stack->AddAnimation(
                    new cStretchAction(
                            node->width*8/7, node->height,
                            node->width, node->height,
                            stack, layerManager,
                            &TmpId, 1 , EASEOUT | ALIGN_RIGHT, 1, 90, node->leadingSpeed/4));

    }
    else if (node->effect & TransitionEffect_DampingOut) {
        stack->AddAnimation(
                    new cStretchAction(
                            node->width, node->height,
                            node->width>>8, node->height,
                            stack, layerManager,
                            &TmpId,1, EASEIN | ALIGN_RIGHT, 1, 0, node->trailingSpeed));
    }

    if (node->effect & TransitionEffect_ZoomIn) {
        stack->AddAnimation(
                    new cStretchAction(
                            node->width>>1, node->height>>1,
                            node->width, node->height,
                            stack, layerManager,
                            &TmpId, 1 , EASEOUT, 1, 0, node->leadingSpeed));
    }
    else if (node->effect & TransitionEffect_ZoomOut) {
        stack->AddAnimation(
                    new cStretchAction(
                            node->width, node->height,
                            node->width>>1, node->height>>1,
                            stack, layerManager,
                            &TmpId,1, EASEIN, 1, 0, node->trailingSpeed));
    }

    return;
}

Boolean AnimationLayerIsEmpty(
    /* [in] */ AnimationLayer* header)
{
    assert(header);

    AnimationLayer* node = (AnimationLayer *)header->Next();
    while (node != header) {
        if (node->alive) {
            return FALSE;
        }
        node = (AnimationLayer *)node->Next();
    }

    return TRUE;
}

ECode Transform(
    /* [in] */ Void* grafixObjectStack)
{
    GrafixObjectStack* stack = (GrafixObjectStack *)grafixObjectStack;
    CGrafixEngine* engine = stack->m_engine;
    BufferCacheNode* primaryBuffer = stack->m_transitionSurface->m_cacheBuffer;
    BitmapBufInfo image;
    Rectangle screen = {0, 0, 0, 0};
    int tx,ty,bx,by;

    LayerManagerEX layerManager(stack->m_width, stack->m_height);

    while (TRUE) {
        if (stack->m_terminate) {
            return NOERROR;
        }

        ECode ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            return ec;
        }

        while (AnimationLayerIsEmpty(&stack->m_animationHeader)) {
            stack->m_enableInput = TRUE;
            g_lock.Unlock();

            WaitResult wr;
            EventState es;
            stack->m_invokeAnimation->Wait(&wr, &es);
            if (stack->m_terminate) {
                return NOERROR;
            }

            ec = g_lock.Lock();
            if (GFX_FAILED(ec)) {
                return ec;
            }
        }

        stack->m_enableInput = FALSE;

        Int32 i = 0;
        Int32 RectNum = 0;

Init:
        layerManager.RemoveAllLayers();
        layerManager.IsTransparent(TRUE);

        image.address = (Address)primaryBuffer->m_buffer.info.address;
        image.width = primaryBuffer->m_buffer.info.width;
        image.height = primaryBuffer->m_buffer.info.height;
        image.pitch = primaryBuffer->m_buffer.info.pitch;
        image.pixelFormat = 0;

        screen.width = primaryBuffer->m_buffer.info.width;
        screen.height = primaryBuffer->m_buffer.info.height;

        layerManager.SetScreenSize(screen.width, screen.height);

        stack->m_layerChanged = FALSE;

        AnimationLayer* node =
            (AnimationLayer *)stack->m_animationHeader.Next();
        while (node != &stack->m_animationHeader) {
            if (node->alive) {

                AddAnime(node, stack, &layerManager);
                i++;
            }

            node = (AnimationLayer *)node->Next();
        }

        if (0 == i) {
            goto Render;
        }

        do {
            stack->m_bActiveFlag = FALSE;
            stack->UpdateAnimation();

            RectNum = layerManager.GetInvalidRegionNum();
            while (RectNum>0) {
                layerManager.GetInvalidRegion(RectNum-1, &tx, &ty, &bx, &by);
                screen.x = tx;
                screen.y = ty;
                screen.width = bx-tx;
                screen.height = by-ty;

                stack->m_engine->m_renderRect.PreciseAdd(&screen);

                RectNum--;
            }

            layerManager.RenderToBuffer(image, RECTANGLE_VALS(&screen));


            // render the composed layer to the screen.
            //
            engine->Render(stack->m_transitionSurface);

            // if m_layerChanged is TRUE, you must do something
            //
            if (stack->m_layerChanged) {
                stack->InternalDeleteAllAnime();
                goto Init;
            }
        } while (stack->m_bActiveFlag);

Render:
        if (!g_isSurfaceFlip) {
            engine->m_renderRect.PreciseAdd(
                &stack->m_primarySurface->m_bornRectangle);
            engine->Render(stack->m_primarySurface);
        }
        else {
            g_needFlipAll = TRUE;
        }

        stack->RemoveAllAnimationLayer();

        stack->m_enableInput = TRUE;

        g_lock.Unlock();
    }
}
